package com.bazhua.bazo.common.wxapplet;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.bazhua.bazo.common.utils.HttpUtil;
import com.bazhua.bazo.common.utils.JSONUtil;
import com.bazhua.bazo.common.wxapplet.model.Code2SessionResponse;
import com.bazhua.bazo.common.wxapplet.model.WxAccountResponse;
import com.bazhua.bazo.common.wxapplet.model.WxAuthorizationResponse;
import com.bazhua.bazo.common.wxapplet.model.WxLoginRequest;
import org.apache.commons.codec.binary.Base64;
import org.apache.shiro.authc.AuthenticationException;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.stereotype.Service;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestTemplate;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.net.URI;
import java.security.AlgorithmParameters;
import java.security.Security;
import java.util.Arrays;

/**
 * @Author: starmile
 * @Description:
 * @Create: 2020-12-21 21:45
 **/
@Service
public class WxAppletService implements IWxAppletService {

    final RestTemplate restTemplate;

    @Value("${wechat.appid}")
    private String appId;

    @Value("${wechat.appsecret}")
    private String appSecret;

    public WxAppletService(RestTemplate restTemplate) {
        this.restTemplate = restTemplate;
    }

    /**
     * 微信的 code2session 接口 获取微信用户信息
     * 官方说明 : https://developers.weixin.qq.com/miniprogram/dev/api/open-api/login/code2Session.html
     */
    private String code2Session(String jsCode) {
        String code2SessionUrl = "https://api.weixin.qq.com/sns/jscode2session";
        MultiValueMap<String, String> params = new LinkedMultiValueMap<>();
        params.add("appid", appId);
        params.add("secret", appSecret);
        params.add("js_code", jsCode);
        params.add("grant_type", "authorization_code");
        URI code2Session = HttpUtil.getURIwithParams(code2SessionUrl, params);
        return restTemplate.exchange(code2Session, HttpMethod.GET, new HttpEntity<String>(new HttpHeaders()), String.class).getBody();
    }


    /**
     * 微信小程序用户登陆，完整流程可参考下面官方地址，本例中是按此流程开发
     * https://developers.weixin.qq.com/miniprogram/dev/framework/open-ability/login.html
     *
     * @param request 小程序端 调用 wx.login 获取到的code,用于调用 微信code2session接口
     * @return 返回后端 自定义登陆态 token  基于JWT实现
     */
    @Override
    public WxAccountResponse wxUserLogin(WxLoginRequest request, Boolean isGetPhone) {
        WxAccountResponse wxAccountResponse = new WxAccountResponse();

        //----------------------------------解密用户信息-----------------------------------------
        String userInfo = null;
        JSONObject userInfoJSON = null;
        try {
            Thread.sleep(500);
            byte[] keyByte = Base64.decodeBase64(request.getSessionKey());
            byte[] dataByte = Base64.decodeBase64(request.getEncryptedData());
            byte[] ivByte = Base64.decodeBase64(request.getIv());
            // 如果密钥不足16位，那么就补足.  这个if中的内容很重要
            int base = 16;
            if (keyByte.length % base != 0) {
                int groups = keyByte.length / base + (keyByte.length % base != 0 ? 1 : 0);
                byte[] temp = new byte[groups * base];
                Arrays.fill(temp, (byte) 0);
                System.arraycopy(keyByte, 0, temp, 0, keyByte.length);
                keyByte = temp;
            }
            Security.addProvider(new BouncyCastleProvider());
            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7Padding", "BC");
            SecretKeySpec spec = new SecretKeySpec(keyByte, "AES");
            AlgorithmParameters parameters = AlgorithmParameters.getInstance("AES");
            parameters.init(new IvParameterSpec(ivByte));
            cipher.init(Cipher.DECRYPT_MODE, spec, parameters);// 初始化
            byte[] resultByte = cipher.doFinal(dataByte);
            if (null != resultByte && resultByte.length > 0) {
                String result = new String(resultByte, "UTF-8");
                userInfoJSON = JSON.parseObject(result);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        wxAccountResponse.setOpenid(request.getOpenid());
        wxAccountResponse.setSessionKey(request.getSessionKey());
        if (isGetPhone) {
            wxAccountResponse.setPhone(userInfoJSON.getString("phoneNumber"));
        }else {
            wxAccountResponse.setNickName(userInfoJSON.getString("nickName"));
            wxAccountResponse.setUrl(userInfoJSON.getString("avatarUrl"));
        }

        return wxAccountResponse;
    }



    @Override
    public WxAuthorizationResponse wxUserAuthorization(String code) {
        WxAuthorizationResponse wxAuthorizationResponse = new WxAuthorizationResponse();
        String resultJson = code2Session(code);
        //2 . 解析数据
        Code2SessionResponse response = JSONUtil.toJavaObject(resultJson, Code2SessionResponse.class);
        if (!response.getErrcode().equals("0"))
            throw new AuthenticationException("code2session失败 : " + response.getErrmsg());
        else {
            wxAuthorizationResponse.setOpenId(response.getOpenid());
            wxAuthorizationResponse.setSessionKey(response.getSession_key());
            return wxAuthorizationResponse;
        }
    }
}
